package com.smartnsoft.fractory.ws;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.FactoryConfigurationError;

import org.fractals.Mandelbrot;
import org.jfract.util.Location;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;

import android.content.Context;
import android.content.res.Resources.NotFoundException;

import com.smartnsoft.droid4me.bo.Business;
import com.smartnsoft.droid4me.cache.Cacher;
import com.smartnsoft.droid4me.cache.Persistence;
import com.smartnsoft.droid4me.cache.Values;
import com.smartnsoft.droid4me.cache.Values.CacheException;
import com.smartnsoft.droid4me.util.StatefulDefaultHandler;
import com.smartnsoft.droid4me.ws.WebServiceCaller;
import com.smartnsoft.fractory.Constants;
import com.smartnsoft.fractory.R;
import com.smartnsoft.fractory.bo.Project;

/**
 * A single point of access to the web services.
 * 
 * @author Willy Noel
 * @since 2011.05.24
 */
public final class FractoryServices
    extends WebServiceCaller
{

  private static volatile FractoryServices instance;

  // We accept the "out-of-order writes" case
  public static FractoryServices getInstance()
  {
    if (instance == null)
    {
      synchronized (FractoryServices.class)
      {
        if (instance == null)
        {
          instance = new FractoryServices();
        }
      }
    }
    return instance;
  }

  private FractoryServices()
  {
  }

  // TODO : see with Edouard the parse of the fracta_model.xml in raw resources
  // WSUriStreamParser<List<Mandelbrot>, Context, SAXException> fractalModelStreamParser = new WSUriStreamParser<List<Mandelbrot>, Context,
  // SAXException>(this)
  // {
  //
  // public com.smartnsoft.droid4me.ws.WSUriStreamParser.UrlWithCallTypeAndBody computeUri(
  // Context context)
  // {
  // return null;
  // }

  public List<Mandelbrot> parse(final Context context, InputStream inputStream)
      throws SAXException
  {
    final List<Mandelbrot> result = new ArrayList<Mandelbrot>();

    final DefaultHandler2 saxHandler = new StatefulDefaultHandler()
    {
      private StringHolder name = new StringHolder();

      private StringHolder type = new StringHolder();

      private StringHolder maxIteration = new StringHolder();

      private StringHolder equation = new StringHolder();

      private String x;

      private String y;

      private String zoom;

      @Override
      protected void onStartElement(String localName, Attributes attributes)
      {
        if (catchCharacters("Implementation", "Fractal", 2, name))
        {
        }
        else if (meetCriteria("Location", "package", 2))
        {
          x = attributes.getValue("x");
          y = attributes.getValue("y");
          zoom = attributes.getValue("zoom");
        }
        else if (catchCharacters("Type", "package", 2, type))
        {
        }
        else if (catchCharacters("MaxIteration", "package", 2, maxIteration))
        {
        }
        else if (catchCharacters("Equation", "package", 2, equation))
        {
        }
      }

      @Override
      protected void onEndElement(String localName)
      {
        if (meetCriteria("Fractal", "Fractory", 1))
        {
          result.add(new Mandelbrot(context, name.get(), equation.get(), Integer.getInteger(type.get()), new Location(Float.valueOf(x), Float.valueOf(y), Float.valueOf(zoom))));

        }
      }
    };
    try
    {
      parseSax(saxHandler, inputStream);
    }
    catch (FactoryConfigurationError exception)
    {
      throw new SAXException("Cannot create a SAX parser!");
    }
    catch (IOException exception)
    {
      throw new SAXException(exception);
    }
    return result;
  }

  //
  // };

  public List<Mandelbrot> getFractalModel(Context context)
      throws CallException, NotFoundException, SAXException
  {
    return parse(context, context.getResources().openRawResource(R.raw.fractal_model));
  }

  @Override
  protected String getUrlEncoding()
  {
    return Constants.WEBSERVICES_HTML_ENCODING;
  }

  private final Cacher<ArrayList<Project>, String, Void, Business.BusinessException, Persistence.PersistenceException, Business.NoUriInputStreamerException> projectsCacher = new Cacher<ArrayList<Project>, String, Void, Business.BusinessException, Persistence.PersistenceException, Business.NoUriInputStreamerException>(new Business.ObjectCached<ArrayList<Project>, Void, Business.NoUriInputStreamerException>(Persistence.getInstance(0))
  {

    public String computeUri(Void parameter)
    {
      return "local://projects";
    }

    public ArrayList<Project> onNullInputStream(Void parameter, String uri)
    {
      return new ArrayList<Project>();
    }

  });

  private final Values.BackedCachedValue<ArrayList<Project>, String, Void, Business.BusinessException, Persistence.PersistenceException, Business.NoUriInputStreamerException> projectsBacked = new Values.BackedCachedValue<ArrayList<Project>, String, Void, Business.BusinessException, Persistence.PersistenceException, Business.NoUriInputStreamerException>(projectsCacher);

  public List<Project> getProjects()
      throws CacheException
  {
    return projectsBacked.getMemoryValue(true, null, null);
  }

  public boolean toggleProjects(Project project)
  {
    final ArrayList<Project> projects = projectsBacked.safeGet(null);
    try
    {
      int index = 0;
      for (Project aProject : projects)
      {
        if (aProject.name.equals(project.name))
        {
          projects.remove(index);
          projects.add(index, project);
          projectsBacked.safeSet(null, projects);
          return false;
        }
        index++;
      }
      // Adds the favorite on the first position
      projects.add(0, project);
    }
    finally
    {
      projectsBacked.safeSet(null, projects);
    }
    return true;
  }

  public boolean testNameProject(Project project)
  {
    final ArrayList<Project> projects = projectsBacked.safeGet(null);
    try
    {
      int index = 0;
      for (Project aProject : projects)
      {
        // TODO: appear a dialog Box to tell to change the name of the project
        if (aProject.name.equals(project.name))
        {
          return false;
        }
        index++;
      }
    }
    finally
    {
      projectsBacked.safeSet(null, projects);
    }
    return true;
  }

  public  void removeProjects( Project project)
  {
    final ArrayList<Project> projects = projectsBacked.safeGet(null);
    try
    {
      int index = 0;
        for (Project aProject : projects)
        {
          if (aProject.name == project.name)
          {
            projects.remove(index);
          }
          index++;
        }
  
    }
    finally
    {
      projectsBacked.safeSet(null, projects);
    }

  }
}
